package com.github.tetrisgame.ui.view;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.view.View;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.github.tetrisgame.R;
import com.github.tetrisgame.utils.UIUtils;

import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class AgreementView extends View {
    //文本的画笔
    private final TextPaint mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
    //记录整个文本解析之后的TextSpan
    private final ArrayList<TextSpan> mHoleSpanArray = new ArrayList<>();
    //显示的文本
    private String mText;
    //显示的行数 -1无数行 或者 具体行数
    private int mMaxLines;
    //文本尺寸
    private int mTextSize;
    //记录所有行的TextSpan组成对象数据
    private ArrayList<ArrayList<TextSpan>> linesArray = new ArrayList<>();
    //富文本对象
    private String mRichString;

    public AgreementView(Context context) {
        this(context, null);
    }

    public AgreementView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public AgreementView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(attrs);
    }

    private void init(AttributeSet attrs) {
        if (attrs != null) {
            TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.AgreementView);
            mText = typedArray.getString(R.styleable.AgreementView_text);
            mMaxLines = typedArray.getInteger(R.styleable.AgreementView_maxLines, -1);
            mTextSize = typedArray.getDimensionPixelSize(R.styleable.AgreementView_textSize, UIUtils.sp2px(16));
            typedArray.recycle();
        }

        //初始化文本画笔的尺寸
        mTextPaint.setTextSize(mTextSize);

        //解析文本
        parseText();
    }

    //使用正则表达式解析出纯文本和特殊样式
    //这是协议<span color='#E85761'>发砥砺</span>奋进阿里的<span color='#00BAAD'>国家立法</span>就阿斯逵荆记录阿是就发了附件<span color='#E85761'>阿里</span>...
    private void parseText() {
        //建立分组正则表达式 解析字段内容 <span color='#E85761'>发砥砺</span>
        //得要颜色和文本
        Pattern pattern = Pattern.compile("\\[span color='(#[a-zA-Z0-9]{6}|[a-zA-Z0-9]{8})'\\](.+?)\\[/span\\]");
        Matcher matcher = pattern.matcher(mText);

        //定义一个变量记录普通文本的索引值
        int normalTextStart = 0;
        int styleTextStart = 0;
        while (matcher.find()) {
            //获取这个样式的颜色
            String color = matcher.group(1);
            //获取文本
            String content = matcher.group(2);
            //获取这次匹配到的文本的起始位置 这个位置就是普通文本的结束位置
            int normalTextEnd = matcher.start();
            //获取从normalTextIndex ~ normalTextEnd 之间的纯文本
            String text = mText.substring(normalTextStart, normalTextEnd);
            //将普通文本封装为对象添加到数组中
            mHoleSpanArray.add(new TextSpan(Color.BLACK, text, mTextPaint));
            //将特殊样式文本封装为对象添加到数组中
            mHoleSpanArray.add(new TextSpan(Color.parseColor(color), content, mTextPaint));
            //将游标定位到下一个普通文本的起始位置
            normalTextStart = matcher.end();
        }
        //可能最后的文本没有添加样式
        if (normalTextStart < mText.length()) {
            mHoleSpanArray.add(new TextSpan(Color.BLACK, mText.substring(normalTextStart), mTextPaint));
        }
    }

    //获取所有文本组成一行的宽度
    private float getHoleTextWidth() {
        float width = 0;
        for (TextSpan span : mHoleSpanArray) {
            width += span.getTextWidth();
        }
        return width;
    }

    //copy数组
    private ArrayList<TextSpan> copyHoleArray() {
        ArrayList<TextSpan> newArray = new ArrayList<>();
        for (TextSpan span : mHoleSpanArray) {
            newArray.add(span.copy());
        }
        return newArray;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        //计算自己想要的尺寸 == 文本的宽度
        int desiredWidth = (int) getHoleTextWidth();

        //获取父容给与的模式和尺寸
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);

        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        int realWidth;
        int realHeight;

        int lines = 1;
        if (widthMode == MeasureSpec.EXACTLY) {
            realWidth = widthSize;
        } else {
            //如果文本的宽度小于父容器的宽度
            if (desiredWidth < widthSize) {
                //宽度以内容为准
                realWidth = desiredWidth;
            } else {
                //默认就是父容器的尺寸
                realWidth = widthSize;
            }
        }

        //先计算折行
        ArrayList<TextSpan> mHoleSpanArrayCopy = copyHoleArray();
        linesArray = new ArrayList<>();
        if (desiredWidth <= realWidth) {
            lines = 1;
            //一行就行显示完毕，数组中只有一个元素
            linesArray.add(mHoleSpanArrayCopy);
        } else {
            //记录当前这一行目前的宽度
            int lineWidth = 0;
            //记录一行的span对象的数组
            ArrayList<TextSpan> lineArray = new ArrayList<>();
            for (int i = 0; i < mHoleSpanArrayCopy.size(); i++) {
                TextSpan span = mHoleSpanArrayCopy.get(i);
                //判断当前宽度+span的宽度是否达到一行
                if (lineWidth + span.getTextWidth() <= realWidth) {
                    //将当前这个span添加到当前行的数组中
                    lineArray.add(span);
                    //记录当前行的宽度
                    lineWidth += span.getTextWidth();
                } else {
                    //说明当前这个span内容过多了，需要分离（这个span需要折行）
                    int count = 1;
                    while (true) {
                        //获取count个元素对应文本宽度
                        int width = (int) span.getSubTextWidth(count);
                        //计算是否达到一行的宽度了
                        if (lineWidth + width > realWidth) {
                            //获取需要折行文本内容
                            String text = span.getSubString(count - 1);
                            //创建新的TextSpan对象
                            lineArray.add(new TextSpan(span.color, text, mTextPaint));
                            //告诉span对象用剩下的内容替换新的内容
                            span.wrapUpdate(count - 1);
                            //返回for循环继续当前这个对象进行判断
                            //不要跳到下一个，因为当前这个有换行
                            i--;
                            //将这一行的数据添加到数组中
                            linesArray.add(lineArray);
                            //下一行的宽度从0开始
                            lineWidth = 0;
                            //为下一行创建新的数组对象
                            lineArray = new ArrayList<>();
                            break;
                        } else {
                            count++;
                        }
                    }
                }
            }

            if (!lineArray.isEmpty()) {
                //将最后一行的数据添加到linesArray中
                linesArray.add(lineArray);
            }

            //当前的行数
            lines = linesArray.size();
        }

        //继续考虑用户设置的行数
        if (mMaxLines > 0) {
            mMaxLines = mMaxLines > lines ? lines : mMaxLines;
        } else {
            mMaxLines = lines;
        }

        if (heightMode == MeasureSpec.EXACTLY) {
            realHeight = heightSize;
        } else {
            Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics();
            realHeight = (int) (mMaxLines * (fontMetrics.bottom - fontMetrics.top));
        }

        //获取真实的宽度尺寸
        setMeasuredDimension(realWidth, realHeight);
    }

    @Override
    protected void onDraw(@NonNull Canvas canvas) {
        Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics();
        //现将maxLines-1行之前的内容完整绘制
        for (int i = 0; i < mMaxLines - 1; i++) {
            //获取这一行数组
            ArrayList<TextSpan> lineArray = linesArray.get(i);
            //依次绘制每一个对象
            float currentWidth = 0;
            for (TextSpan span : lineArray) {
                //设置这个样式的颜色
                mTextPaint.setColor(span.color);
                canvas.drawText(
                        span.text,
                        currentWidth,
                        Math.abs(fontMetrics.top) + i * (fontMetrics.bottom - fontMetrics.top),
                        mTextPaint);

                currentWidth += span.getTextWidth();
            }
        }

        //考虑最后一行
        ArrayList<TextSpan> lastLineArray = linesArray.get(linesArray.size() - 1);
        if (mMaxLines < linesArray.size()) {
            //显示行数 < 总数，最后一行需要在末尾添加...
            //获取最后一行的数据
            lastLineArray = linesArray.get(mMaxLines - 1);
            //计算。。。的宽度
            float dotWidth = mTextPaint.measureText("...");

            //从这一行的最后一个元素依次往前找
            for (int i = lastLineArray.size() - 1; i >= 0; i--) {
                //获取i前面的所有span对象的宽度
                float currentWidth = 0;
                for (int j = 0; j < i; j++) {
                    currentWidth += lastLineArray.get(j).getTextWidth();
                }
                //获取这一行的最后一个TextSpan对象
                TextSpan lastSpan = lastLineArray.get(i);
                //从文本的倒数元素开始替换
                int count = lastSpan.text.length();
                for (; count > 0; count--) {
                    //获取从0-j对应元素的宽度
                    float spanWidth = lastSpan.getSubTextWidth(count);
                    if (currentWidth + spanWidth + dotWidth <= getWidth()) {
                        //抛弃多余内容
                        lastSpan.text = lastSpan.getSubString(count);

                        //添加一个。。。对象
                        lastLineArray.add(new TextSpan(Color.BLACK, "...", mTextPaint));
                        break;
                    }
                }
                //判断上一个for循环出来的情况
                if (count == 0) {
                    //说明去掉span都不够
                    lastLineArray.remove(lastLineArray.size() - 1);
                } else {
                    //已经找到位置了
                    break;
                }
            }

        }

        //绘制最后一行
        float currentWidth = 0;
        for (TextSpan span : lastLineArray) {
            //设置这个样式的颜色
            mTextPaint.setColor(span.color);
            canvas.drawText(
                    span.text,
                    currentWidth,
                    Math.abs(fontMetrics.top) + (mMaxLines - 1) * (fontMetrics.bottom - fontMetrics.top),
                    mTextPaint);

            currentWidth += span.getTextWidth();

        }
    }

    /*
    内部类管理每个样式对应的文本和颜色
     */
    class TextSpan {
        private final TextPaint mPaint;
        public int color;
        public String text;
        private float mTextWidth = 0;

        public TextSpan(int color, String text, TextPaint paint) {
            this.color = color;
            this.text = text;
            this.mPaint = paint;

            //预先测量一次
            getTextWidth();
        }

        //获取当前文本的宽度
        public float getTextWidth() {
            if (mTextWidth == 0) {
                mTextWidth = mPaint.measureText(text);
            }
            return mTextWidth;
        }

        //获取部分文本的宽度
        public float getSubTextWidth(int count) {
            if (count < 0 || count > text.length()) return -1;

            //获取0-(count-1)的子字符串
            String subString = text.substring(0, count);
            //获取这部分字符串的尺寸
            return mPaint.measureText(subString);
        }

        //获取子字符串
        public String getSubString(int end) {
            mTextWidth = 0;
            return text.substring(0, end);
        }

        //触发内容更新
        public void wrapUpdate(int start) {
            String newString = text.substring(start);
            text = newString;
            mTextWidth = 0;
        }

        public TextSpan copy() {
            return new TextSpan(color, text, mTextPaint);
        }
    }
}


















